package com.wzf.greattruth.zookeeper;

import java.io.File;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.commons.lang3.StringUtils;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import com.wzf.greattruth.zookeeper.helper.ZookeeperHelper;
import com.wzf.greattruth.zookeeper.node.Node;

public class ZookeeperManager {
    private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperManager.class);

    private static final List<ACL> acl = ZooDefs.Ids.OPEN_ACL_UNSAFE;

    public static ZookeeperManager instance = new ZookeeperManager();

    public static ZookeeperManager getInstance() {
        return instance;
    }

    private ZookeeperManager() {
        super();
    }

    public boolean createDirIfNotExist(ZooKeeper zookeeper, String targetDir) {
        CreateMode mode = CreateMode.PERSISTENT;
        return createDirIfNotExist(zookeeper, targetDir, mode);
    }

    /**
     * 检查目录是否存在，不存在则创建
     * 
     * @param zookeeper
     * @param targetDir
     * @param mode
     * @return
     */
    public boolean createDirIfNotExist(ZooKeeper zookeeper, String targetDir, CreateMode mode) {
        targetDir = ZookeeperHelper.removeLastSeparator(targetDir);
        // 检查partargetDirentDir是否存在
        try {
            Stat stat = zookeeper.exists(targetDir, false);
            if (stat != null) {
                return true;
            }
        } catch (KeeperException | InterruptedException e) {
            LOGGER.error("check zkDir exist failed! parentDir={}", targetDir, e);
            return false;
        }

        if (mode == null) {
            mode = CreateMode.PERSISTENT;
        }

        byte[] datas = null;
        // 分别创建targetDir的各级目录
        String[] dirs = targetDir.split(File.separator);
        StringBuffer sb = new StringBuffer(File.separator);
        for (String dir : dirs) {
            if (StringUtils.isBlank(dir)) {
                continue;
            }
            sb.append(dir);
            String zkDir = sb.toString();
            try {
                Stat stat1 = zookeeper.exists(zkDir, false);
                if (stat1 == null) {
                    String nodePath = createNode(zookeeper, zkDir, datas, mode);
                    if (StringUtils.isBlank(nodePath)) {
                        return false;
                    }
                }
            } catch (KeeperException | InterruptedException e) {
                LOGGER.error("check or create node failed! zkDir={}", zkDir, e);
                return false;
            }
            sb.append(File.separator);
        }
        return true;
    }

    /**
     * 创建节点
     * <li>如果创建的是{@link CreateMode.PERSISTENT_SEQUENTIAL} 或者
     * {@link CreateMode.EPHEMERAL_SEQUENTIAL} 类型的节点，请注意检查是否需要在dir结尾处添加
     * {@link File.separator}
     * 
     * @param dir
     * @param datas
     * @param mode
     * @return
     */
    public String createNode(ZooKeeper zookeeper, String dir, byte[] datas, CreateMode mode) {
        String path = null;
        try {
            String nodePath = ZookeeperHelper.removeLastSeparator(dir);
            path = zookeeper.create(nodePath, datas, acl, mode);
        } catch (KeeperException | InterruptedException e) {
            LOGGER.error("create node failed! nodePath={} mode={}", dir, mode, e);
            return null;
        }
        return path;
    }

    /**
     * 创建目录
     * 
     * @param zookeeper
     * @param dir
     * @param datas
     * @param mode
     * @return
     */
    public String createDir(ZooKeeper zookeeper, String dir, byte[] datas, CreateMode mode) {
        String path = null;
        try {
            String nodePath = ZookeeperHelper.removeLastSeparator(dir);
            path = zookeeper.create(nodePath, datas, acl, mode);
        } catch (KeeperException | InterruptedException e) {
            LOGGER.error("create node failed! nodePath={} mode={}", dir, mode, e);
            e.printStackTrace();
            return null;
        }
        return path;
    }

    /**
     * 删除节点
     * 
     * @param zookeeper
     * @param nodePath
     * @return
     */
    public boolean deleteNode(ZooKeeper zookeeper, String nodePath) {
        try {
            zookeeper.delete(nodePath, -1);
            return true;
        } catch (Exception e) {
            LOGGER.error("delete node failed! nodePath={}", nodePath, e);
            return false;
        }
    }

    /**
     * 查询节点
     * 
     * @param zookeeper
     * @param parentDir
     * @return
     */
    public SortedSet<Node> getChildren(ZooKeeper zookeeper, String parentDir, String nodePrefix) {
        // 查询父目录下的所有children，排序后找到上一个node，监听此node的create/delete事件
        List<String> children = null;
        try {
            children = zookeeper.getChildren(parentDir, false);
        } catch (KeeperException | InterruptedException e) {
            LOGGER.error("get children failed!", e);
        }
        if (CollectionUtils.isEmpty(children)) {
            return null;
        }
        SortedSet<Node> nodes = new TreeSet<Node>();
        for (String child : children) {
            nodes.add(new Node(parentDir, child, nodePrefix));
        }
        return nodes;
    }

    /**
     * 返回最小的哪一个节点
     * 
     * @param zookeeper
     * @param parentDir
     * @return
     */
    public Node getMinNode(ZooKeeper zookeeper, String parentDir, String nodePrefix) {
        SortedSet<Node> nodes = getChildren(zookeeper, parentDir, nodePrefix);
        if (nodes == null) {
            return null;
        }
        return nodes.first();
    }

    /**
     * 获取节点数据
     * 
     * @param zookeeper
     * @param node
     * @return
     */
    public byte[] getDatas(ZooKeeper zookeeper, Node node) {
        if (node == null || StringUtils.isBlank(node.getId())) {
            return null;
        }
        byte[] datas = getDatas(zookeeper, node, false);
        return datas;
    }

    /**
     * 获取节点数据，根据{@code deleteNode}决定获取数据后是否删除节点
     * 
     * @param zookeeper
     * @param node
     * @param deleteNode
     * @return
     */
    public byte[] getDatas(ZooKeeper zookeeper, Node node, boolean deleteNode) {
        if (node == null || StringUtils.isBlank(node.getId())) {
            return null;
        }
        byte[] datas = null;
        try {
            String nodePath = ZookeeperHelper.getNodePath(node.getDir(), node.getId());
            // 获取数据
            datas = zookeeper.getData(nodePath, false, null);

            if (deleteNode) {
                // 删除数据
                zookeeper.delete(nodePath, -1);
            }
        } catch (KeeperException | InterruptedException e) {
            LOGGER.error("get node data failed!", e);
        }
        return datas;
    }

    public boolean updateDatas(ZooKeeper zookeeper, Node node, byte[] datas) {
        if (node == null || StringUtils.isBlank(node.getId())) {
            return false;
        }
        try {
            String nodePath = ZookeeperHelper.getNodePath(node.getDir(), node.getId());
            // 更新节点数据
            int version = -1;
            Stat stat = zookeeper.setData(nodePath, datas, version);
            if (stat != null) {
                return true;
            }

        } catch (KeeperException | InterruptedException e) {
            LOGGER.error("get node data failed!", e);
        }
        return false;
    }

    /**
     * 节点是否存在
     * 
     * @param zookeeper
     * @param nodePath
     * @return
     */
    public boolean exist(ZooKeeper zookeeper, String nodePath) {
        return exist(zookeeper, nodePath, null);
    }

    /**
     * 节点是否存在，并根据参数绑定监控事件
     * 
     * @param zookeeper
     * @param nodePath
     * @param waitWatcher
     * @return
     */
    public boolean exist(ZooKeeper zookeeper, String nodePath, Watcher waitWatcher) {
        try {

            nodePath = ZookeeperHelper.removeLastSeparator(nodePath);
            Stat stat = null;
            if (waitWatcher != null) {
                stat = zookeeper.exists(nodePath, waitWatcher);
            } else {
                stat = zookeeper.exists(nodePath, false);
            }
            if (stat != null) {
                return true;
            }
        } catch (KeeperException | InterruptedException e) {
            LOGGER.error(" node data failed!", e);
        }
        return false;
    }

}
